package rabbitmq

import (
	"encoding/json"
	"fmt"
	"github.com/rabbitmq/amqp091-go"
	"iris_product/datamodels"
	"iris_product/services"
	"log"
	"strconv"
)

type Message struct {
	UserID int64
	ProductId int64
}

const AMQP_URL = "amqp://hengbo:aA111222333@127.0.0.1:5672/miaosha"

type Rabbitmq struct {
	con *amqp091.Connection
	channel *amqp091.Channel
	Exchange string
	QueueName string
	RoutingKey string
}

// 创建 amqp 基础实例
func NewRabbitmq(exchange,queueName,routineKey string) *Rabbitmq {
	var (
		err error
	)
	rabbitmq := &Rabbitmq{
		con:        nil,
		channel:    nil,
		Exchange:   exchange,
		QueueName:  queueName,
		RoutingKey: routineKey,
	}

	rabbitmq.con,err = amqp091.Dial(AMQP_URL)
	rabbitmq.failErr(err,"连接amqp失败")
	rabbitmq.channel,err = rabbitmq.con.Channel()
	rabbitmq.failErr(err,"从amqp中取channel失败")

	return rabbitmq

}

// 创建简单实例模式
func NewSimpleRabbitmq(queueName string) *Rabbitmq {
	return NewRabbitmq("",queueName,"")
}


func (r *Rabbitmq) PushMessage(message string) {
	var (
		err error
	)
	// 申请队列,如果不存在会自动创建
	_,err = r.channel.QueueDeclare(
		r.QueueName,
		// 是否持久化存储消息
		false,
		// 是否自动删除消息,如果为true,消费者断开,消息会自动删除
		false,
		// 是否只当前账号可见
		false,
		// 是否阻塞,发送消息后是否等待服务器的响应
		false,
		// 其他参数
		nil,
	)
	if err != nil {
		log.Printf("申请队列失败:" + err.Error())
	}
	// 推送消息
	err = r.channel.Publish(
		r.Exchange,
		r.QueueName,
		// 如果为true,根据exchange,和routingKey没有找到对应的队列时,会将消息返还给发送者
		false,
		// 如果为true,当exchange发送到消息队列后发现没有绑定消费者,会将消息返还给发送者
		false,
		amqp091.Publishing{
			ContentType: "text/plain",
			Body: []byte(message),
		},

	)
	if err != nil {
		log.Printf("推送消息失败 " + err.Error())
	}

}

// 消费消息
func (r *Rabbitmq) CustomerMessage(orderService services.IOrderService,productService services.IProductService) {
	// 申请队列
	var (
		err error
		zhuShe chan string
	)
	// 申请队列,如果不存在会自动创建
	_,err = r.channel.QueueDeclare(
		r.QueueName,
		// 是否持久化存储消息
		false,
		// 是否自动删除消息,如果为true,消费者断开,消息会自动删除
		false,
		// 是否只当前账号可见
		false,
		// 是否阻塞,发送消息后是否等待服务器的响应
		false,
		// 其他参数
		nil,
	)
	if err != nil {
		log.Printf("申请队列失败:" + err.Error())
	}

	// 取消息
	msgChan,err := r.channel.Consume(
		r.QueueName,
		// 用来区分多个消费者,不区分传空
		"",
		// 是否自动应答
		false,
		// 是否只当前账号可见
		false,
		// 如果设置为true,同一个amqp的连接发送的消息,不能传递给同一个连接进行消费
		false,
		// 队列消费是否不阻塞, (这一个消息消费完了下一个消息再进来)
		false,
		nil,
	)
	if err != nil {
		log.Printf("从amqp中取消息失败:" + err.Error())
	}

	// 设置qos
	err = r.channel.Qos(
		1, // 当前消费者一次能接受的最大消息数量
		0,  // 服务器传递的最大容量(以8个字节为单位)
		false, // 如果设置为true 对整个mq的 channel 生效
		)
	if err != nil {
		log.Printf("设置amqp的qos失败:" + err.Error())
	}

	// 阻塞一下
	zhuShe = make(chan string)
	go func() {
		for d := range msgChan {
			fmt.Println("消费了消息:" + string(d.Body))

			message := &Message{}
			err := json.Unmarshal(d.Body,message)
			if err != nil {
				log.Printf("解析消费的消息错误:" + err.Error())
			}

			// 减少商品数量
			err = productService.SubProductNum(message.ProductId)
			if err != nil {
				log.Printf("减少商品数量失败:" + err.Error())
			}
			orderInfo := &datamodels.Order{
				UserId:      message.UserID,
				ProductId:   message.ProductId,
				OrderStatus: 0,
			}
			// 插入订单
			id,err := orderService.InsertOrder(orderInfo)
			if err != nil {
				log.Printf("插入订单失败:" + err.Error())
			}
			log.Println("消费订单成功,插入的订单ID为:" + strconv.FormatInt(id,10))
			// 手动应答
			// 为false表示确认当前消息 如果为true表示确认所有未确认的消息
			d.Ack(false)

		}
	}()
	fmt.Println("启动消费者成功")
	<-zhuShe
}

// 关闭amqp连接
func (r *Rabbitmq) failErr(err error,message string) {
	if err != nil {
		log.Printf("%s : %s",message,err)
		panic(fmt.Sprintf("%s : %s",message,err))
	}
}
